/**
 * 预定义的聊天消息
 * （预先建立好，在有需要的时候发送）
 */
import { type RequiredFields } from '@gnlab/utils/lang'

/**
 * 可用的聊天消息类型
 */
export enum WordType {
  Text = 'text', // 纯文本
  Images = 'image', // 图片（因为历史问题，值要用 image）
  Video = 'video', // 视频
  Audio = 'voice', // 语音
  File = 'file', // 文件
  LinkCard = 'link', // 超链接卡片
  PersonCard = 'personCard', // 联系人名片
  MiniProgram = 'mp', // 小程序
  Videonum = 'videonum', // 视频号
  GroupInvite = 'intoRoom', // 群邀请
  GroupAt = 'roomAt', // 群 @ 所有人
}

export type WordTypes = {
  [WordType.Text]: TextWord
  [WordType.Images]: ImagesWord
  [WordType.Video]: VideoWord
  [WordType.Audio]: AudioWord
  [WordType.File]: FileWord
  [WordType.LinkCard]: LinkCardWord
  [WordType.PersonCard]: PersonCardWord
  [WordType.MiniProgram]: MiniProgramWord
  [WordType.Videonum]: VideonumWord
  [WordType.GroupInvite]: RequiredFields<GroupInviteWord, 'IsSingle' | 'MemberCount'>
  [WordType.GroupAt]: GroupAtWord
}

export type Word = WordTypes[keyof WordTypes]

/**
 * 各类消息的中文标题
 */
type Titles = { [type in WordType]: string }
export const wordTitleMap: Titles = {
  [WordType.Text]: '文本',
  [WordType.Images]: '图片',
  [WordType.Video]: '视频',
  [WordType.Audio]: '语音',
  [WordType.File]: '文件',
  [WordType.LinkCard]: '链接卡片',
  [WordType.PersonCard]: '名片',
  [WordType.MiniProgram]: '小程序',
  [WordType.Videonum]: '视频号',
  [WordType.GroupInvite]: '群邀请',
  [WordType.GroupAt]: '@所有人',
}

/**
 * 可在所有场景下发送的消息类型
 * （其他类型如“群邀请”只在特定场景下可发送）
 */
export const generalWordTypes = [
  WordType.Text,
  WordType.Images,
  WordType.Video,
  // WordType.Audio,  // 发送语音会触发风控，暂时禁用
  WordType.File,
  WordType.LinkCard,
  WordType.PersonCard,
  WordType.MiniProgram,
  WordType.Videonum,
]

/**
 * 各类消息的数据格式
 */
export interface BaseWord {
  type: WordType
}
export interface TextWord extends BaseWord {
  type: WordType.Text
  text: string
}
export interface ImagesWord extends BaseWord {
  type: WordType.Images
  images: string[]
}
export interface VideoWord {
  type: WordType.Video
  url: string
}
export interface AudioWord {
  type: WordType.Audio
  url: string
}
export interface FileWord extends BaseWord {
  type: WordType.File
  name: string
  url: string
}
export interface LinkCardWord extends BaseWord {
  type: WordType.LinkCard
  desc: string
  image_url: string
  title: string
  url: string
}
export interface PersonCardWord extends BaseWord {
  type: WordType.PersonCard
  avatar: string
  nickname: string
  user_id: string
  corpid: string
  corpname: string
}
export interface MiniProgramWord extends BaseWord {
  type: WordType.MiniProgram
  username: string // gh_3a18fa53cb80@app
  appid: string // wx723bb5bb25362f6c
  appname: string
  appicon: string
  title: string
  page_path: string
  cover_path: string
  // 数据里有，但是后端不需要的字段
  // appinfo: string, // from_msgid_6197600668870351583
  // thumb_height: number,
  // thumb_width: number,
}
export interface VideonumWord extends BaseWord {
  type: WordType.Videonum
  nickname: string // 视频号名称
  avatar: string // 视频号头像
  desc: string // 消息标题
  cover_url: string // 封面图
  thumb_url: string // 缩略图
  extras: string
  feed_type: number
  url: string
  // 数据里有但未使用的值
  // object_id: string
  // object_nonce_id: string
}
export interface GroupInviteWord extends BaseWord {
  type: WordType.GroupInvite
  ApplyMessage: string
  Rooms: string[]
  IsSingle?: boolean // queue 是否队列加群（每次只向一个群发邀请，前一个群成员满一定人数后才向下一个群发邀请）
  MemberCount?: number // queueMemberLimit 开启“队列加群”时，成员满多少人向下个群发邀请
  // 各选中群的基本信息，用于预览群邀请内容（因为部分群可能不在当前账号的企业微信下，拉取其信息成本比较高）。
  // 此为新增字段，对于老数据，仍需通过 Rooms 结合本地联系人列表拉取群信息
  roomInfos?: { conversationId: string; name: string; isExternal: boolean }[]
}
export interface GroupAtWord extends BaseWord {
  type: WordType.GroupAt
  text: string
}

/**
 * 各类消息的空白模板
 */
export const emptyWords: WordTypes = {
  [WordType.Text]: {
    type: WordType.Text,
    text: '',
  },
  [WordType.Images]: {
    type: WordType.Images,
    images: [],
  },
  [WordType.Video]: {
    type: WordType.Video,
    url: '',
  },
  [WordType.Audio]: {
    type: WordType.Audio,
    url: '',
  },
  [WordType.File]: {
    type: WordType.File,
    name: '',
    url: '',
  },
  [WordType.LinkCard]: {
    type: WordType.LinkCard,
    desc: '',
    image_url: '',
    title: '',
    url: '',
  },
  [WordType.PersonCard]: {
    type: WordType.PersonCard,
    avatar: '',
    nickname: '',
    user_id: '',
    corpid: '',
    corpname: '',
  },
  [WordType.MiniProgram]: {
    type: WordType.MiniProgram,
    username: '',
    appid: '',
    appname: '',
    appicon: '',
    title: '',
    page_path: '',
    cover_path: '',
  },
  [WordType.Videonum]: {
    type: WordType.Videonum,
    nickname: '',
    avatar: '',
    desc: '',
    cover_url: '',
    thumb_url: '',
    extras: '',
    feed_type: 0,
    url: '',
  },
  [WordType.GroupInvite]: {
    type: WordType.GroupInvite,
    ApplyMessage: '',
    Rooms: [],
    IsSingle: false,
    MemberCount: 300,
    roomInfos: [],
  },
  [WordType.GroupAt]: {
    type: WordType.GroupAt,
    text: '',
  },
}

/**
 * 把历史遗留的 Word 数据转换为最新版本
 */
export function formatIrregularWord<T extends { type: WordType }>(word: T) {
  if (word.type === WordType.Videonum) {
    // 此版本转换后，可正常显示，但无法发送
    interface OldVideonumWord {
      type: WordType.Videonum
      title: string
      logo_url: string
      image_url: string
      videonum_url: string
      videonum_data: string
    }
    const raw = word as unknown as OldVideonumWord | VideonumWord
    const formatted: VideonumWord =
      'desc' in raw
        ? raw
        : {
            type: raw.type,
            nickname: raw.title,
            avatar: raw.logo_url,
            desc: raw.title,
            cover_url: raw.image_url,
            thumb_url: raw.image_url,
            extras: raw.videonum_data,
            feed_type: 0,
            url: raw.videonum_url,
          }
    return formatted
  }
  return word as unknown as Word
}

/**
 * 把各类 Word 对象转换成字符串（供预览或搜索）
 */
const stringifyCache = new WeakMap<Word, string>()
export function stringifyWord(word: Word) {
  if (!stringifyCache.has(word)) stringifyCache.set(word, realStringifyWord(word))
  return stringifyCache.get(word)!
}
function realStringifyWord(word: Word): string {
  switch (word.type) {
    case WordType.Text:
      return word.text
    case WordType.LinkCard:
      return word.title
    case WordType.File:
      return word.name
    case WordType.MiniProgram:
      return word.title
    case WordType.PersonCard:
      return word.nickname
    case WordType.Videonum:
      return word.desc
    case WordType.GroupAt:
      return word.text
    default:
      return ''
  }
}

/**
 * 快速生成一个文本 Word
 */
export function makeTextWord(text: string): Word {
  return {
    type: WordType.Text as const,
    text,
  }
}
